import sys
import pandas as pd
import csv
import re
from rv32_inst_parser import parse_rv32_inst
from riscv_debug import parse_dtmcs, parse_mdi_data_in, parse_mdi_data_out

ir_cmds = {
    0x00: ['BYPASS', 1],  # Bypass register
    0x1f: ['BYPASS', 1],  # Bypass register
    0x01: ['IDCODE', 32],  # ID code register
    0x10: ['DTMCS', 32],  # Debug port access register
    0x11: ['DMI', 41],  # Access port access register
    0x12: ['BYPASS', 1],  # Reserved (BYPASS)
    0x13: ['BYPASS', 1],  # Reserved (BYPASS)
    0x14: ['BYPASS', 1],  # Reserved (BYPASS)
    0x15: ['BYPASS', 1],  # Reserved (BYPASS)
    0x16: ['BYPASS', 1],  # Reserved (BYPASS)
    0x17: ['BYPASS', 1],  # Reserved (BYPASS)
}

debug_rom_table = [
    0x00c0006f,0x0180006f, 
    0x00c0006f,0x0080006f,
    0x00c0006f,0x18802623, 
    0x7b349073,0x7b241073, 
    0xf1402473,0x18002483,
    0x18802223,0x00047413,
    0x4004c493,0x02848a63,
    0x18002483,0x00848c63,
    0x7b202473,0x18802223,
    0x00100073,0x7b3024f3,
    0x0ff0000f,0x18802423,
    0x7b3024f3,0x7b202473,
    0x18902023,0x7b200073,
    0x7b3024f3,0x7b202473,
    0x00100073,0x10000067,]

IR_LEN = 5
IR_IDCODE_VAL = 0x01
IR_DTMCS_VAL = 0x10
IR_DMI_VAL = 0x11

args_map = {
    '--parse_inst': 3,
    '--parse_jtag_seq': 4,
    '--parse_debug_rom':2,
}


def version():
    return '1.0'


def show_usage():
    print('riscv_util version ' + version())
    print('Usage:-----------------------------------------------')
    print('    --parse_inst <inst_word>')
    print('    --parse_jtag_seq <tdi_csv> <tdo_csv> ')
    print('    --parse_debug_rom')


def parse_full_jtag_sequence(path, defaultCmd=IR_DMI_VAL):
    with open(path) as f:
        reader = csv.reader(f)
        print(type(reader))

        cur_cmd = IR_IDCODE_VAL
        cmd_stage = 'CMD'
        data_in = 0
        data_out = 0
        tab_count = 2
        dmiCmdIsRead = False

        logfile = open("full_log.txt", "w")

        for i, row in enumerate(reader):
            if i > 0:
                seq_id = row[0]
                tdi_str = row[2]
                tdo_str = row[5]

                # Get the TDI and TO data
                tdi_hex = int(re.findall(r'[(](.*?)[)]', tdi_str)[0], 16)
                tdo_hex = int(re.findall(r'[(](.*?)[)]', tdo_str)[0], 16)

                # If the TDI data is IR instruction
                if (tdi_str.startswith('IR TDI:')):
                    cur_cmd = tdi_hex & 0x1F
                    cmd_stage = 'CMD'
                # If the TDI data represents DATA
                if (tdi_str.startswith('DR TDI:')):
                    cmd_stage = 'DATA'

                if (cmd_stage == 'DATA'):
                    # Handle output
                    cmd_str = ir_cmds.get(cur_cmd, ['Unknown', 0xFF])[0]
                    # IDCODE information
                    if (cmd_str == 'BYPASS'):
                        print(seq_id + ' - ' + cmd_str + ': ', file=logfile)
                        print('    Read: ', hex(tdo_hex), file=logfile)
                        print('    Write:', hex(tdi_hex), file=logfile)
                        cur_cmd = defaultCmd
                    if (cmd_str == 'IDCODE'):
                        print(seq_id + ' - ' + "Read", cmd_str, file=logfile)
                        data_out = tdo_hex
                        for i in (0, 1):
                            print('   ', i, hex(data_out & 0xffffffff), file=logfile)
                            data_out = data_out >> 32
                    # DTMCS information
                    elif (cmd_str == 'DTMCS'):
                        print(seq_id + ' - ' + cmd_str, file=logfile)
                        print('    Read: ', parse_dtmcs(tdo_hex, False), file=logfile)
                        print('    Write:', parse_dtmcs(tdi_hex, True), file=logfile)
                    # DMI information
                    else:
                        opIn, dataIn, addrIn, dataInInfo = parse_mdi_data_in(tdi_hex)
                        ack, dataOut, addrOut, dataOutInfo = parse_mdi_data_out(tdo_hex)
                        if dmiCmdIsRead:
                            print(seq_id + ' - ', dataOutInfo, file=logfile)
                            # print (seq_id + ' - ', dataOutInfo, '(origin = ' + hex(tdo_hex) + ')', file=logfile)
                            dmiCmdIsRead = False
                        elif (opIn == 1):
                            dmiCmdIsRead = True
                        else:
                            # print (cmd_str + ': ', file=logfile)
                            print(seq_id + ' - ', dataInInfo, '(origin = ' + hex(tdi_hex) + ')', file=logfile)


def main(argv):
    if len(argv) < 2:
        show_usage()
    else:
        # noinspection PyPep8Naming
        argCount = args_map.get(argv[1], 0xffff)
        if argCount == 0xffff:
            show_usage()
        else:
            if argv[1].startswith('--parse_inst'):
                data = int(argv[2], 16)
                instStr = parse_rv32_inst(data)
                print(instStr)

            elif argv[1].startswith('--parse_jtag_seq'):
                tdi_df = pd.read_csv(argv[2])
                tdo_df = pd.read_csv(argv[3])
                merge_df = pd.concat([tdi_df, tdo_df], axis=1, join='inner')
                merged_file = 'decoder_full.csv'
                mergingInfo = "Merging {} and {} into {}...".format(argv[2], argv[3], merged_file)
                print(mergingInfo)
                merge_df.to_csv(merged_file)
                print('Parsing {}...'.format(merged_file))
                parse_full_jtag_sequence(merged_file)
                print('Completed!')
            elif argv[1].startswith('--parse_debug_rom'):
                offset = 0
                for elem in debug_rom_table:
                    instStr = parse_rv32_inst(int(elem))
                    print(hex(offset) + " " + hex(elem) + ' : ' + instStr)
                    offset +=4
            else:
                show_usage()


if __name__ == '__main__':
    main(sys.argv)
